#include "wrap.h"
#include <sys/epoll.h>

#define SERV_PORT 8888
#define MAX_SIZE 1024

void epoll_server(){

		int n,i,j,ret,cfd,lfd, epfd;					// epfd为监听红黑树的树根
		char buf[4096], str[INET_ADDRSTRLEN];			// str用来存放客户端的IP地址和端口号
		int sockfd;
		struct sockaddr_in serv_addr, clit_addr;
		socklen_t clit_addr_len;


		struct epoll_event tep, ep[MAX_SIZE];		// 其中tep用来设置单个fd属性，ep是epoll_wait()传出的满足监听事件的数组
		
		// 1. 先创建socket函数
		lfd = Socket(AF_INET, SOCK_STREAM, 0);


		// 设置端口复用，避免2MSL
		int opt = 1;
		setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, (void *)&opt, sizeof(opt));

		// 2. 使用bind函数进行绑定
		serv_addr.sin_family = AF_INET;
		serv_addr.sin_port = htons(SERV_PORT);
		serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
		Bind(lfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));

		// 3. 使用listen函数设置监听上限
		Listen(lfd, MAX_SIZE);

		// 4. 使用epoll_create函数创建红黑树树根
		epfd = epoll_create(MAX_SIZE);

		// 5. 将lfd添加到监听红黑树中,需要先初始化lfd
		tep.events = EPOLLIN;
		tep.data.fd = lfd;

		epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &tep);

		while(1){
			// 6. 进行阻塞监听
			ret = epoll_wait(epfd, ep, MAX_SIZE, -1);

			// 7. 判断是否满足监听事件
			if(ret < 0){
				perror("epoll_wait error");
				exit(1);
			}else{
				for(i = 0; i < ret; ++i){
					sockfd = ep[i].data.fd;
					// 判断lfd 是否满足读事件，如果满足，说明有新的客户端发起请求连接
					if(sockfd == lfd){
						clit_addr_len = sizeof(clit_addr);
						cfd = Accept(sockfd, (struct sockaddr*)&clit_addr, &clit_addr_len);
						
						printf("client %d connect succ! ip: %s,  port: %d\n", cfd, inet_ntop(AF_INET, &clit_addr.sin_addr.s_addr, str, INET_ADDRSTRLEN),
									   	ntohs(clit_addr.sin_port));

						// 将cfd添加到红黑树的监听节点中
						tep.events = EPOLLIN;
						tep.data.fd = cfd;
						epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &tep);
					}else{
						// 此处表明cfd满足读时间，有客户端进行数据的读写
						n = Read(sockfd, buf, sizeof(buf));
						if(n == 0){
							close(sockfd);
							epoll_ctl(epfd, EPOLL_CTL_DEL, sockfd, NULL);	// 将关闭的cfd，从监听节点上摘下
						}else if(n < 0){
							perror("read error!");
							epoll_ctl(epfd, EPOLL_CTL_DEL, sockfd, NULL);	// 将关闭的cfd，从监听节点上摘下
							exit(1);
						}else{
							Write(STDOUT_FILENO, buf, n);
							for(j = 0; j<n; ++j){
								buf[j] = toupper(buf[j]);
							}
							Write(sockfd, buf, n);
						}
					}
				}
			}
		}

}

int main(int argc, char *argv[]){
	epoll_server();
}
